#include "iconcache.h"
#include <QtCore>

int qHash(const QSize & key)
{
    return key.width() * 10000 + key.height();
}

IconCache::IconCache(QObject *parent) :
    QObject(parent), m_quit(false)
{
    int count = QThread::idealThreadCount();
    for(int i=0; i<count; i++) {
        Worker * worker = new Worker(this, i != 0);
        m_threads.append(worker);
        worker->start();//QThread::LowestPriority);
    }
}

IconCache::~IconCache()
{
    m_mutex.lock();
    m_quit = true;
    m_cond.wakeAll();
    m_mutex.unlock();
    for(int i=0; i<m_threads.size(); i++) {
        m_threads[i]->wait();
        delete m_threads[i];
    }
}

QSharedPointer<QPixmap> IconCache::load(const MediaItemPtr & item, const QSize &size)
{
    QueueItem qi;
    qi.item = item;
    qi.filename = item->metadata().filename;
    qi.size = size;
    QMutexLocker l(&m_mutex);
    QPair<MediaItemPtr, QSize> p(qMakePair(item, size));
    if(m_queueset.contains(p)) {
        m_queueset.remove(p);
        m_queue.removeAll(qi);
    }
    l.unlock();
    QSharedPointer<QPixmap> pm = load(item->metadata().filename, size);
    emit loaded(item, size, pm);
    return pm;
}

void IconCache::enqueue(const MediaItemPtr & item, const QSize & size)
{
    QueueItem qi;
    qi.item = item;
    qi.filename = item->metadata().filename;
    qi.size = size;
    QMutexLocker l(&m_mutex);
    QPair<MediaItemPtr, QSize> p(qMakePair(item, size));
    if(!m_queueset.contains(p)) {
        if(size.isEmpty())
            m_queue.push_back(qi);
        else
            m_queue.push_front(qi);
        m_queueset.insert(p);
        m_cond.wakeAll();
    }
}

QSharedPointer<QPixmap> IconCache::load(const QString & filename, const QSize & size)
{
    //if(size.isEmpty())
    //    ::sleep(1);
    QImage img = m_engine.load(filename);
    if(img.isNull() || size.isEmpty())
        return QSharedPointer<QPixmap>();

    if(size.width() <= 128 && size.height() <= 128) {
        img = img.scaled(size, Qt::KeepAspectRatioByExpanding);
        img = img.copy((img.width()-size.width()) / 2, (img.height() - size.height())/2, size.width(), size.height());
        addRoundCorners(img, 10);
    }
    else
        img = img.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    QSharedPointer<QPixmap> pm(new QPixmap(QPixmap::fromImage(img)));

    // insert shared pointer into cache
    QMutexLocker l(&m_mutex);
    m_cache.append(pm);
    while(m_cache.size() > 1000)
        m_cache.pop_front();

    return pm;
}

int IconCache::queueSize()
{
    QMutexLocker l(&m_mutex);
    return m_queue.size();
}

void IconCache::run(bool highpriority)
{
    QMutexLocker l(&m_mutex);
    while(!m_quit) {
        if(!m_queue.empty() && (!highpriority || !m_queue.first().size.isEmpty())) {
            QueueItem item = m_queue.takeFirst();
            QPair<MediaItemPtr, QSize> p(qMakePair(item.item, item.size));
            m_queueset.remove(p);
            l.unlock();
            QSharedPointer<QPixmap> pm = load(item.filename, item.size);
            emit loaded(item.item, item.size, pm);
            l.relock();
            continue;
        }
        m_cond.wait(&m_mutex);
    }
}

void IconCache::addRoundCorners(QImage & img, int radius)
{
    if(img.format() != QImage::Format_ARGB32)
        img = img.convertToFormat(QImage::Format_ARGB32);
    for(int x=0; x<radius; x++) for(int y=0; y<radius; y++) {
        int d2 = (radius-x)*(radius-x) + (radius-y)*(radius-y);
        if(d2 > radius * radius) {
            double d = sqrt((double)d2);
            long alpha = 255-255 * std::min(1.0, d - radius);

            QRgb rgb = img.pixel(x, y);
            img.setPixel(x, y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha));

            rgb = img.pixel(img.width() - 1 - x, y);
            img.setPixel(img.width() - 1 - x, y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha));

            rgb = img.pixel(x, img.height() - 1 - y);
            img.setPixel(x, img.height() - 1 - y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha));

            rgb = img.pixel(img.width() - 1 - x, img.height() - 1 - y);
            img.setPixel(img.width() - 1 - x, img.height() - 1 - y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha));
        }
    }
}

IconCache::Worker::Worker(IconCache * cache, bool highpriority)
    : m_cache(cache), m_highpriority(highpriority)
{
}

void IconCache::Worker::run()
{
    m_cache->run(m_highpriority);
}
